package com.beantools.execption;

import com.beantools.constants.SingleChar;

import java.io.Serializable;

import static com.beantools.constants.SingleChar.BLANK_OP;
import static com.beantools.util.ObjectUtil.isNotNull;
import static com.beantools.util.StringUtil.isBlank;

/**
 * 该类是处理与属性相关的异常，其继承{@code RuntimeException}.
 * 比如，在将属性封装为方法时，若属性的第一个字母是小写，第二个字母是大写
 * 程序生成set方法和get方法会有问题，如下代码所示：
 * <pre>
 *     public class Test {
 *          private String nAme;
 *          public String getPropertyName() {return nAme;}
 *          public void setPropertyName(String nAme) {this.nAme = nAme;}
 *     }
 * </pre>
 * set和get后的首字母大写，但上面的方法错误，这样的就需要抛出错误
 *
 * @author baoya
 * @since 1.1.0
 */

public class PropertyException extends RuntimeException implements Serializable{

    private static final long serialVersionUID = 3584443319479537885L;
    /**
     * 表示特定的类字面常量，如果调用者传递该类，
     * 获取该类的类路径，用于指明是哪个类出现了错误
     */
    private Class<?> persistentClazz;

    /**
     * 属性名称，比如Test类的nAme
     */
    private String propertyName;

    /**
     * 抛出的异常信息，比如属性名nAme的第二字母不能大写
     */
    private String message;

    /**
     * 实例化异常的构造器，其有一个参数。{@code propertyName} 表示哪个属性出现了错误。
     * 如果属性名nAme的第二个字母大写，抛出这样的异常<strong>throw new PropertyException
     * ("属性名nAme的第二个字母不能大写","nAme")</strong>
     *
     * @param propertyName 属性名称
     */
    public PropertyException( String propertyName) {
        this.propertyName = propertyName;
    }

    /**
     * 实例化异常的构造器，其有两个参数，{@code message}是需要抛出具体的异常信息;
     * {@code propertyName} 表示哪个属性出现了错误。如果属性名nAme的第二个字母大写，
     * 抛出这样的异常<strong>throw new PropertyException("属性名nAme的第二个字母不能
     * 大写","nAme")</strong>
     *
     * @param message      抛出的异常信息
     * @param propertyName 属性名称
     * @since 1.1.0
     */
    public PropertyException(String message, String propertyName) {
        super(message);
        this.message = message;
        this.propertyName = propertyName;
    }

    /**
     * 实例化异常的构造器，其有两个参数，{@code message}需要抛出具体的异常信息;
     * {@code propertyName} 表示哪个属性出现了错误;{@code persistentClazz}是当前
     * 类字面常量。如果属性名nAme的第二个字母大写，抛出这样的异常<strong>throw
     * new PropertyException("性名nAme的第二个字母不能大写","nAme")</strong>
     *
     * @param message         抛出的异常信息
     * @param propertyName    属性名称
     * @param persistentClazz 调用者传递的类字面常量
     * @since 1.1.0
     */
    public PropertyException(String message, String propertyName, Class<?> persistentClazz) {
        super(message);
        this.message = message;
        this.propertyName = propertyName;
        this.persistentClazz = persistentClazz;
    }

    /**
     * 重写父类的方getMessage()法，返回自定义的错误信息。首先，属性名不能为空
     * 否则，就抛出空指针异常。如果当前类的类字面常量不为空，则抛出了异常，比如：
     * <small>
     * "类com.beantools.Test的属性名为aName的第二个字母不能大写
     * </small>
     * 如果调用者没有写当前类字面常量，其就会这样报出错误信息
     * <small>
     * "属性名为aName的第二个字母不能大写
     * </small>
     *
     * @return message 错误信息
     */
    @Override
    public String getMessage() {
        if (isBlank(propertyName)) {
            throw new NullPointerException("属性名不能为空");
        }
        return (isNotNull(persistentClazz) ? ("类" + persistentClazz.getName() + "的") : SingleChar.NULL_OP) +
                "属性名为" + BLANK_OP + propertyName + BLANK_OP + "的异常信息" +
                SingleChar.SIGN_COLON_OP +BLANK_OP + this.message;
    }
}
